﻿/// <reference path="jquery.js" />
/// <reference path="jquery.ba-resize.js" />
/// <reference path="jquery.mousewheel.js" />
/// <reference path="LiberUI.Base.js" />


//#region 对象的UI模型基类
function Model() {
    /// <summary>
    /// 创建一个模型描述业务对象在界面展示的风格
    /// </summary>
    /// <returns type=""></returns>
    var instance = this;
    this.originalObj = null;
    this.currentObj = null;
    this.member = {};
    this.evHandlers = {};
    this.isDirty = false;

    instance.member.vf_getTypeName = function () {
        return (typeof instance).toString();
    };
    instance.member.vf_getTitle = function (_field) {
        return _field;
    }
    instance.member.vf_convert = function (_field, _value) {
        return instance.currentObj[_field];
    };
    instance.member.vf_reConvert = function (_field, _displayValue) {
        return instance.currentObj[_field];
    };
    instance.member.vf_actionFlag = function (_action) {
        return true;
    };

    return this;
};
Model.prototype = {
    GetTypeName:function(){
        return this.member.vf_getTypeName();
    },
    Bind: function (_obj) {
        /// <summary>
        /// 绑定业务对象实例
        /// </summary>
        /// <param name="_obj"></param>
        this.originalObj = $.extend({}, _obj);
        this.currentObj = _obj;
        return this;
    },
    GetTitle: function (_field) {
        /// <summary>
        /// 获取属性的界面标题
        /// </summary>
        /// <param name="_field"></param>
        if (ObjHelper.IsEmpty(_field)) {
            return "";
        }
        else {
            return this.member.vf_getTitle(_field);
        }
    },
    GetFriendlyValue: function (_field) {
        /// <summary>
        /// 获取属性值的界面友好形式
        /// </summary>
        /// <param name="_field"></param>
        return this.member.vf_convert(_field, this.currentObj[_field]);
    },
    SetValue: function (_field, _value, _needCnvert) {
        /// <summary>
        /// 设置某个属性的值
        /// </summary>
        /// <param name="_field"></param>
        /// <param name="_value"></param>
        /// <param name="_needCnvert">如果是以友好形式提供的值，调用内部的形式转换</param>
        if (this.currentObj[_field]==undefined) {
            return this;
        }
        if (_needCnvert) {
            this.currentObj[_field] = this.member.vf_reConvert(_value);
        }
        else {
            this.currentObj[_field] = _value;
        }

        //TODO:发出事件通知
        var ev = this._fireEvent("ObjectChanged");
        if (ev.isDefaultPrevented()) {
            this.currentObj[_field] = this.originalObj[_field];
        }
        else {
            this.isDirty = true;
        }
        return this;
    },
    IsDirty: function () {
        /// <summary>
        /// 业务对象的属性是否已经被修改
        /// </summary>
        return this.isDirty;
    },
    Save:function(){
        /// <summary>
        /// 保存对属性的改动
        /// </summary>
        this.isDirty = false;
        this.originalObj = $.extend({}, this.currentObj);
        //TODO:发出事件通知
        this._fireEvent('ObjectUpdated');
        return this;
    },
    ActionFlag: function (_action) {
        /// <summary>
        /// 判定对于特定的业务对象实例，某种操作是否允许
        /// </summary>
        /// <param name="_action"></param>
        return this.member.vf_actionFlag(_action);
    },
    on: function (_event, _fn) {
        if (ObjHelper.IsEmpty(_event) || !ObjHelper.IsFunc(_fn)) {
            return this;
        }
        if (!this.evHandlers[_event]) {
            this.evHandlers[_event] = [];
        }
        this.evHandlers[_event].push(_fn);
        return this;
    },
    off: function (_event, _fn) {
        if (ObjHelper.IsEmpty(_event) || !ObjHelper.IsFunc(_fn)) {
            return this;
        }
        var hs = this._member.evHandlers[_event];
        if (hs) {
            for (var i = 0; i < hs.length; i++) {
                if (hs[i] === _fn) {
                    hs[i] = undefined;
                    hs.splice(i, 1);
                    i--;
                }
            }
            if (hs.length == 0) {
                this._member.evHandlers[_event] = undefined;
            }
        }
        return this;
    },
    _fireEvent: function (_event,data) {
        var ev = new jQuery.Event(_event, data);
        if (this.evHandlers[_event]) {
            ev.target = this;
            for (var i = 0; i < this._member.evHandlers[_event].length; i++) {
                var f = this.evHandlers[_event][i];
                if (f) {
                    f.call(this, ev);
                }
                if (ev.isPropagationStopped && ev.isPropagationStopped == true) {
                    break;
                }
            }
        }
        return ev;
    }
}
//#endregion

//#region 基本控件基类
function Controller(_$srcElm,_opt) {
	/// <summary>
	/// UI基础控件基类
	/// </summary>
	/// <param name="_$srcElm" type="JQuery Object">UI控件所基于的DOM元素</param>
    /// <returns type="Controller">控件实例</returns>
    var instance = this;
    this.Option =$.extend({Focusble:true,Propagation:false}, _opt);
    this._ini(_$srcElm);
    if (instance.Focusble === true) {
        instance._member.$rootElm.attr("tabindex", 0);
    }
    var needRebindEvent = false;

    function onDomFocus(ev) {
        if (instance.IsDisabled) {
            return;
        }
        if (instance.Option.Focusble !== true) {
            instance._member.$rootElm.removeAttr("tabindex");
            instance._member.$rootElm.blur();
            return;
        }
        instance.IsFocused = true;
        instance._member.vf_onFocus();
        instance._fireEvent("focus");
    };

    function onDomBlur(ev) {
        if (instance.IsDisabled) {
            return this;
        }
        instance.IsFocused = false;
        if (instance.Option.Propagation == true || ev.target == instance._member.$rootElm[0]) {
            instance._member.vf_onBlur();
            instance._fireEvent("blur");
        }

    };

    function onClick(ev) {
        if (ev && ev.preventDefault) {
            //ev.preventDefault();
        }
        if (instance.IsDisabled) {
            return this;
        }
        if (instance.Option.Propagation == true || ev.target == instance._member.$rootElm[0]) {
            instance._member.vf_onClick(ev);
            instance._fireEvent("click");
        }
        return this;
    };

    function onDbClick(ev) {
        if (ev && ev.preventDefault) {
            ev.preventDefault();
        }
        if (instance.IsDisabled) {
            return this;
        }
        if (instance.Option.Propagation == true || ev.target == instance._member.$rootElm[0]) {
            instance._member.vf_onDblClick(ev);
            instance._fireEvent("dblclick");
        }
        return this;
    };

    function onKeySpace(ev) {
        if (instance && instance.IsFocused === true) {
            instance.click();
        }
    };

    this.Focusble = function () {
        return instance.Option.Focusble;
    };

    this.af_getValue = function (_v) {
        /// <summary>
        /// 由具体的控件实例覆盖其实现，用于获取控件的当前值
        /// </summary>
        /// <returns type=""></returns>
        return _v;
    };

    this.af_setValue = function (_newValue) {
        /// <summary>
        /// 由具体的控件实例负责实现，负责设置控件的值，并返回最终的设置结果(主要用于决定值的呈现方式，使得同样的控件类型可以具有灵活的表现形式)
        /// </summary>
        /// <param name="_newValue"></param>
        /// <returns type=""></returns>
        return _newValue;
    };

    instance.onShow = function () {
        if (needRebindEvent == true) {
            needRebindEvent = false;
            this._member.$rootElm.on("click", onClick);
            this._member.$rootElm.on("dblclick", onDbClick);
            this._member.$rootElm.on("focus", onDomFocus);
            this._member.$rootElm.on("blur", onDomBlur);
        }
        this._member.vf_onShow();
        this._member.$rootElm.show();
        this._fireEvent("show");
    };
    instance.onHide = function () {
        needRebindEvent = true;
        this._member.$rootElm.hide();
        this._member.vf_onHide();
        this._fireEvent("hide");
    };

    //#region 由子类实现的虚函数
    instance._member.vf_disable = function (_message) {

    };

    instance._member.vf_enable = function () {

    };

    instance._member.vf_onClick = function (ev) {

    };

    instance._member.vf_onDblClick = function (ev) {

    };

    instance._member.vf_reset = function () {

    };

    instance._member.vf_onFocus = function (ev) {
    	/// <summary>
    	/// 由子类实现在获取焦点时的额外处理（注意，不要在执行根Dom元素的focus方法，会导致循环调用）
    	/// </summary>
    	/// <param name="ev"></param>
    };

    instance._member.vf_onBlur = function (ev) {

    };

    instance._member.vf_onDispose = function () {
        instance._member.$rootElm.off("click", onClick);
        instance._member.$rootElm.off("focus", onDomFocus);
        instance._member.$rootElm.on("dblclick", onDbClick);
        instance._member.$rootElm.off("blur", onDomBlur);
        KeybordHelper.globalOff("space", onKeySpace);
        ObjHelper.DisposeObj(instance);
        instance = null;
    };

    instance._member.vf_onShow = function () {

    };

    instance._member.vf_onHide = function () {

    };

    instance._member.vf_onHostChanged = function () {

    };
    //#endregion

    this.IsDisabled = false;
    this.IsDisposed = false;
    this.IsFocused = false;
    this.Visible = true;

    this.dispose = function () {
        if (this.IsDisposed) {
            return;
        }
        this.IsDisposed = true;
        this._fireEvent("disposed");
        this._member.$rootElm.off("click", instance.onClick);
        this._member.$rootElm.off("dblclick", instance.onDbClick);
        this._member.$rootElm.off("focus", instance.onDomFocus);
        this._member.$rootElm.off("blur", instance.onDomBlur);
        KeybordHelper.globalOff("space", instance.onKeySpace);
        this._member.vf_onDispose();
        ObjHelper.DisposeObj(this._member);
        ObjHelper.DisposeObj(instance);
        delete this._member;
        delete this.Option;
        instance = null;
    };

    this._member.$rootElm.on("click", onClick);
    this._member.$rootElm.on("dblclick", onDbClick);
    this._member.$rootElm.on("focus", onDomFocus);
    this._member.$rootElm.on("blur", onDomBlur);
    KeybordHelper.globalOn("space", onKeySpace);
    return this;
}

Controller.prototype._fireEvent = function (event, data) {
    var ev = new jQuery.Event(event, data);
    if (this._member.evHandlers[event]) {
        ev.target = this;
        for (var i = 0; i < this._member.evHandlers[event].length;i++) {
            var f = this._member.evHandlers[event][i];
            if (f)
            {
                f.call(this, ev);
            }
            if (ev.isPropagationStopped&&ev.isPropagationStopped==true) {
                break;
            }
        }
    }
    return ev;
}

Controller.prototype.on = function (event, fn) {
    if (ObjHelper.IsEmpty(event) || !ObjHelper.IsFunc(fn)) {
        return this;
    }
    if (!this._member.evHandlers[event]) {
        this._member.evHandlers[event] = [];
    }
    this._member.evHandlers[event].push(fn);
    return this;
}

Controller.prototype.off = function (event, fn) {
    if (ObjHelper.IsEmpty(event) || !ObjHelper.IsFunc(fn)) {
        return this;
    }
    var hs = this._member.evHandlers[event];
    if (hs) {
        for (var i = 0; i < hs.length; i++) {
            if (hs[i] === fn) {
                hs[i] = undefined;
                hs.splice(i, 1);
                i--;
            }
        }
        if (hs.length == 0) {
            this._member.evHandlers[event] = undefined;
        }
    }
    return this;
}

Controller.prototype.dom = function (_hostDom) {
    if (ObjHelper.IsElement(_hostDom)) {
        this._member.$rootElm.appendTo(_hostDom);
        this._member.vf_onHostChanged();
        this._fireEvent("hostChanged");
        return this;
    }
    else {
        return this._member.$rootElm;
    }
}

Controller.prototype.ID = function (_newId) {
    if (ObjHelper.IsEmpty(_newId)) {
        return this._member.$rootElm.attr("id");
    }
    else {
        this._member.$rootElm.attr("id", _newId);
        return this;
    }
}

Controller.prototype.enable = function () {
    if (this.IsDisabled) {
        this.IsDisabled = false;
        this._member.$rootElm.removeClass('disabled');
        this._member.vf_enable();
        this._fireEvent("enabled");
    }
    return this;
}

Controller.prototype.disable = function (message) {
    /// <summary>
    /// 禁用当前控件
    /// </summary>
    /// <param name="message">期望给出的提示信息(显示方式由控件决定)</param>
    /// <returns type=""></returns>
    if (!this.IsDisabled) {
        this.IsDisabled = true;
        this._member.$rootElm.addClass('disabled');
        this._member.vf_disable(message);
        this._fireEvent("disabled");
    }
    return this;
}

Controller.prototype.focus = function () {
    if (this.IsDisabled || this.IsDisposed||!this.Option.Focusble) {
        return this;
    }
    this.IsFocused = true;
    this._member.vf_onFocus();
    this._fireEvent("focus");
    this._member.$rootElm[0].focus();
    return this;
}

Controller.prototype.blur = function () {
    if (this.IsDisabled || this.IsDisposed) {
        return this;
    }
    this.IsFocused = false;
    this._member.vf_onBlur();
    this._fireEvent("blur");
    this._member.$rootElm[0].blur();
    return this;
}

Controller.prototype.select = function () {
    if (this.IsDisabled || this.IsDisposed || !this.Option.Focusble) {
        return this;
    }
    this._member.$rootElm[0].select();
    return this;
}

Controller.prototype.click = function () {
    if (this.IsDisabled) {
        return this;
    }
    this._member.vf_onClick();
    this._fireEvent("click");
    return this;
}

Controller.prototype.attr = function (key, value) {
	/// <summary>
	/// 获取或设置指定属性
	/// </summary>
	/// <param name="key">属性名称</param>
    /// <param name="value">属性的值</param>
    if (ObjHelper.IsEmpty(value)) {
        return this._member.$rootElm.attr(key);
    }
    else {
        this._member.$rootElm.attr(key, value);
        return this;
    }
};

Controller.prototype.addClass = function (_styleClass) {
    /// <summary>
    /// 给控件的主体部分添加额外的样式类【注意，请确保控件的不同交互状态下也定义了相应的视觉效果，否则可能出现不符合预期的视觉效果】
    /// </summary>
    /// <param name="_styleClass"></param>
    /// <returns type=""></returns>
    this._member.$rootElm.addClass(_styleClass);
    return this;
};

Controller.prototype.removeClass = function (_styleClass) {
    this._member.$rootElm.removeClass(_styleClass);
    return this;
}

Controller.prototype.val = function (_newValue, _fireEvent) {
    if (_newValue===null||_newValue===undefined) {
        return this.af_getValue(this._member.Value);
    }
    else if (_fireEvent===undefined||_fireEvent===null || _fireEvent === true) {
        var e = this._fireEvent("valueChanging", { NextValue: _newValue });
        if (e.isDefaultPrevented && e.isDefaultPrevented == true) {
            return this;
        }
        this._member.Value = _newValue;
        this.af_setValue(this._member.Value);
        this._fireEvent("valueChanged", { Value: this._member.Value});
        return this;
    }
    else {
        this._member.Value = _newValue;
        this.af_setValue(this._member.Value);
        return this;
    }
}

Controller.prototype.reset = function () {
    this._member.vf_reset();
    return this;
}

Controller.prototype.show = function () {
    this.Visible = true;
    this.onShow();
    return this;
};

Controller.prototype.hide = function () {
    this.Visible = false;
    this.onHide();
    return this;
}

Controller.prototype._ini = function (_$srcElm) {
    this._member = {};
    this.instance = this;
    this._member.evHandlers = {};
    this._member.Value = {};

    if (ObjHelper.IsEmpty(_$srcElm)) {
        _$srcElm = $("<div></div");
    }
    if (!_$srcElm.attr("id")) {
        _$srcElm.attr("id", "id" + Math.random());
    }
    this._member.$rootElm = _$srcElm;
}

//#endregion

//#region 标准按钮控件
function Button(_$srcElm,_opt) {
    Controller.apply(this,[_$srcElm,_opt]);
    var instance = this;
    var ttl = null;
    this.Option = $.extend({}, ButtonOption, this.Option);
    this._member.$rootElm.addClass('Button').addClass(this.Option.ExStyle);
    if (this.Option.HasShine) {
        this._member.$rootElm.prepend($("<div class='shine'></div>"));
    }
    ttl = ObjHelper.IsEmpty(_$srcElm)?"":_$srcElm.text();
    this.Title = function (_title) {
        if (!ObjHelper.IsEmpty(_title)) {
            ttl = _title;
            instance._member.$rootElm.text(_title);
        }
        return this;
    };
    instance._member.vf_onFocus = function () {
    }

    instance._member.vf_enable = function () {
        instance._member.$rootElm.text(ttl);
        instance._member.$rootElm.removeClass("disabled");
    };

    instance._member.vf_disable = function (msg) {
        if (msg) {
            instance._member.$rootElm.text(msg);
        }
        instance._member.$rootElm.addClass("disabled");
    };

    instance._member.vf_onDispose=function() {
        instance = null;
    }
    this.constructor = Button;
    return this;
}
Button.prototype = Controller.prototype;
var ButtonOption = {
    HasShine: true
}

//#endregion

//#region Checkbox
function CheckBox(_$srcElm, _opt) {
    Controller.apply(this, [_$srcElm, _opt]);
    var instance = this;
    this.Option = $.extend({}, this.Option, CheckBoxOption, _opt);
    this._member.$rootElm.addClass('CheckBox').addClass(instance.Option.ExStyle);

    instance.af_setValue = function (_newValue) {
        switch (_newValue) {
            case "checked":
                instance._member.$rootElm.removeClass(instance.Option.StyleOfUnchecked+" "+instance.Option.StyleOfPartchecked).addClass(instance.Option.StyleOfChecked);
                break;
            case "unchecked":
                instance._member.$rootElm.removeClass(instance.Option.PartCheckble+" "+instance.Option.StyleOfChecked).addClass(instance.Option.StyleOfUnchecked);
                break;
            case "partChecked":
                if (!instance.Option.PartCheckble) {
                    break;
                }
                instance._member.$rootElm.removeClass(instance.Option.StyleOfChecked+" "+instance.Option.StyleOfUnchecked).addClass(instance.Option.StyleOfPartchecked);
                break;
            default:
                _newValue = "unchecked";
                instance._member.$rootElm.removeClass(instance.Option.PartCheckble + " " + instance.Option.StyleOfChecked).addClass(instance.Option.StyleOfUnchecked);
                break;
        }
    }

    instance._member.vf_onClick = function (ev) {
        switch (instance._member.Value) {
            case "":
            case "unchecked":
                instance.val("checked");
                break;
            case "checked":
                instance.val("unchecked");
                break;
            case "partChecked":
                instance.val("unchecked");
                break;

        }
    }

    instance._member.vf_disable = function () {
        instance._member.$rootElm.addClass("disabled");
    };

    instance._member.vf_enable = function () {
        instance._member.$rootElm.removeClass("disabled");
    };

    this.val("unchecked", false);
    this.constructor = CheckBox;
    return this;
}
CheckBox.prototype = Controller.prototype;
var CheckBoxOption = {
    ExStyle: "",
    StyleOfChecked: 'checked',
    StyleOfUnchecked: 'unchecked',
    StyleOfPartchecked: 'partChecked',
    PartCheckble:"false" //是否允许部分选中状态
}
//#endregion

//#region 滚动条
function Scroller(_$srcElm, _opt) {
	/// <summary>
	/// 创建一个滚动条控件
	/// </summary>
	/// <param name="_$srcElm">要滚动的内容元素[滚动条会出现在其父元素的右边缘]</param>
	/// <param name="_opt"></param>
	/// <returns type="Scroller">滚动条实例</returns>
    Controller.apply(this, [_$srcElm, _opt]);
    var instance = this;
    var dragHolder = 0;
    this.Option = $.extend({}, ScrollerOption, this.Option, _opt);
    ScrollerHelper.Ini(this._member, this.Option);
    onResize();

    instance._member.$host.css('overflow', 'hidden').css('padding-right', instance._member.$VBar.width());
    
    this._member.deltaY = 0;
    this._member.deltaX = 0;
    this._member.scrolling = null;
    this._member.speed = instance.Option.AutoSpeed ? this._member.TotalHeight/1000 : instance.Option.WheelSpeed;

    function onResize(ev) {
        if (instance._member.$host.length > 0) {
            ScrollerHelper.Resize(instance._member);
            instance._member.Value = { ScrollTop: instance._member.$host[0].scrollTop, ScrollLeft: 0 };
            instance._fireEvent("scrolling", instance._member.Value);
        }
    };

    function onVClick(ev) {
        if (dragHolder != 0) {
            dragHolder = 0;
            return;
        }
        ev.offsetY //即鼠标点击点在滑杆上的纵向下移位置
        var s = ev.offsetY / instance._member.ViewHeight * instance._member.TotalHeight;
        doMove(s, 0);
        //ScrollerHelper.Repose(instance._member);
        //instance._fireEvent('scrolling', {ScrollTop:s});
    };

    function onHandClick(ev) {
        ev.stopPropagation();
    };

    function onDragBeginY(ev) {
        dragHolder = ev.clientY;
        var _member = instance._member;
        $('html').on("mousemove", onDragingY).on("mouseup", onDragEndY);
        instance._member.$host.on("mouseup", onDragEndY);
        instance._member.$VBar.on("mouseup", onDragEndY);
    };

    function onDragingY(ev) {
        ev.preventDefault();
        var moveY = ev.clientY - dragHolder;  //鼠标相对拖动开始的纵向移动距离
        dragHolder = ev.clientY;
        moveY = moveY / instance._member.ViewHeight * instance._member.TotalHeight;  //鼠标移动距离转换为可滚动区域的移动距离
        var sTop = moveY + instance._member.$host[0].scrollTop;

        doMove(sTop, 0);
    };

    function onDragEndY(ev) {
        $('html').off("mousemove", onDragingY).off("mouseup", onDragEndY);
        instance._member.$host.off("mouseup", onDragEndY);
        instance._member.$VBar.off("mouseup", onDragEndY);
    };

    function onWheel(ev) {
        if (ev && ev.preventDefault && instance._member.Visible === true) {
            if (ev.ctrlKey == true||ev.altKey==true) {
                return;
            }
            ev.preventDefault();
            ev.stopPropagation();
        }

        if (ev.originalEvent.deltaY) {
            if (ev.originalEvent.deltaY > 0) {
                if (instance._member.deltaY > 0) {
                    instance._member.deltaY += 1;
                }
                else {
                    instance._member.deltaY = 1;
                }
            }
            else if (ev.originalEvent.deltaY < 0) {
                if (instance._member.deltaY < 0) {
                    instance._member.deltaY -= 1;
                }
                else {
                    instance._member.deltaY = -1;
                }
            }
        }
        else if (ev.originalEvent.wheelDelta) {
            if (ev.originalEvent.wheelDelta < 0) {
                if (instance._member.deltaY >= 0) {
                    instance._member.deltaY += 1;
                }
                else {
                    instance._member.deltaY = 0;
                }

            }
            else if (ev.originalEvent.wheelDelta > 0) {
                if (instance._member.deltaY < 0) {
                    instance._member.deltaY -= 1;
                }
                else {
                    instance._member.deltaY = -1;
                }
            }
        }
        else {
            return;
        }
        if (instance._member.scrolling == null) {
            instance._member.scrolling = setTimeout(vScroll);
        }
    };

    function vScroll() {
        var speed = instance._member.speed;
        while (instance._member.deltaY > 0) {
            instance._member.deltaY--;
            var sTop = instance._member.$host[0].scrollTop + 20 * speed;
            doMove(sTop);
        }
        while (instance._member.deltaY < 0) {
            instance._member.deltaY++;
            var sTop = instance._member.$host[0].scrollTop - 20 * speed;
            doMove(sTop);
        }
        clearTimeout(instance._member.scrolling);
        instance._member.scrolling = null;
    };

    function doMove(_sTop, _sLeft, _ani) {
        if (_sTop > instance._member.TotalHeight - instance._member.ViewHeight) {
            _sTop = instance._member.TotalHeight - instance._member.ViewHeight;
            instance._member.Value = { ScrollTop: _sTop, ScrollLeft: 0 };
            if (!_ani) {
                instance._member.$host[0].scrollTop = _sTop;
                instance._fireEvent("scrolling", instance._member.Value);
            }
            else {
                instance._member.$host.clearQueue().animate({ scrollTop: _sTop }, { speed: 'fast', easing: 'swing', step: function () { ScrollerHelper.Repose(instance._member); instance._fireEvent("scrolling", instance._member.Value); } });
            }

            instance._fireEvent('getBottom', instance._member.Value);
            onResize();
        }
        else if (_sTop <= 0) {
            _sTop = 0;
            instance._member.Value = { ScrollTop: _sTop, ScrollLeft: 0 };
            if (!_ani) {
                instance._member.$host[0].scrollTop = _sTop;
                instance._fireEvent("scrolling", instance._member.Value);
            }
            else {
                instance._member.$host.clearQueue().animate({ scrollTop: _sTop }, { speed: 'fast', easing: 'swing', step: function () { ScrollerHelper.Repose(instance._member); instance._fireEvent("scrolling", instance._member.Value); } });
            }

            instance._fireEvent('getTop', instance._member.Value);
        }
        else {
            instance._member.Value = { ScrollTop: _sTop, ScrollLeft: 0 };
            if (!_ani) {
                instance._member.$host[0].scrollTop = _sTop;
                instance._fireEvent("scrolling", instance._member.Value);
            }
            else {
                instance._member.$host.clearQueue().animate({ scrollTop: _sTop }, { speed: 'fast', easing: 'swing', step: function () { ScrollerHelper.Repose(instance._member); instance._fireEvent("scrolling", instance._member.Value); } });
            }

        }
        ScrollerHelper.Repose(instance._member);
    };

    instance.af_setValue = function (_newValue) {
        if ($.isNumeric(_newValue['ScrollTop'])) {
            doMove(_newValue.ScrollTop, 0, true);
        }
    };

    instance._member.vf_onDispose = function () {
        instance._member.$host.off("resize", onResize).off("mousewheel", onWheel);
        instance._member.$rootElm.off("resize", onResize);
        instance._member.$VBar.off("click", onVClick);
        instance._member.$VHand.off("click", onHandClick).off("mousedown", onDragBeginY);
        $('html').off("mousemove", onDragBeginY).off("mouseup", onDragEndY);
        instance._member.$host = null;
        instance._member.$rootElm = null;
        ObjHelper.DisposeObj(instance);
    };

    this.Offset = function () {
        /// <summary>
        /// 获取当前滚动容器已经滚动的纵向距离
        /// </summary>
        /// <returns type=""></returns>
        return { top: instance._member.$host[0].scrollTop };
    };

    this.Reset = function () {
        onResize();
        ScrollerHelper.Repose(instance._member);
        return instance;
    };

    this._member.$host.on("resize", onResize).on("mousewheel", onWheel);
    this._member.$rootElm.on("resize", onResize);
    this._member.$VBar.on("click", onVClick);
    this._member.$VHand.on("click", onHandClick).on("mousedown", onDragBeginY);
    this._member.$VBar.on('show', instance.Reset);
    this.constructor = Scroller;
    return this;
}
Scroller.prototype = Controller.prototype;
var ScrollerOption={
    VBarStyle: "VScrollbar",
    VHandStyle: "VHand",
    WheelSpeed: 1,  //滚动速度
    AutoSpeed:false  //是否根据内容长度自动调节滚动速度,每增长1000px，速度增加1倍
}
var ScrollerHelper = {
    Ini: function (_member,_opt) {
        _member.$host = _member.$rootElm.parent();
        _member.$VBar = $("<div></div>").addClass(_opt.VBarStyle).prependTo(_member.$host);
        _member.$VHand = $("<div></div>").addClass(_opt.VHandStyle).appendTo(_member.$VBar);

        var cssHeight = _member.$host.css('height');
        var cssMaxHeight = _member.$host.css('max-height');
        var ovy = _member.$host.css('overflow-y');
        if (!cssHeight & !cssMaxHeight) {
            console.warn('滚动条控件要求容器控件必须指定height或max-height，否则由于容器高度会自动扩展，永远无法显示出来');
        }
    },

    Resize: function (_member) {
        _member.TotalHeight = _member.$rootElm[0].scrollHeight;
        _member.TotalWidth = _member.$rootElm[0].scrollWidth;
        _member.ViewHeight = _member.$host.height();
        _member.ViewWidth = _member.$host.width();

        if (_member.TotalHeight <= _member.ViewHeight) {
            _member.$VBar.hide();
            _member.Visible = false;
        }
        else {
            _member.Visible = true;
            _member.$VBar.show();
            _member.$VBar.height(_member.ViewHeight);
            _member.$VHand.height(_member.ViewHeight * _member.ViewHeight / _member.TotalHeight);
        }
    },

    Repose: function (_member) {
        var _sTop = _member.$host[0].scrollTop;
        var offsetY = _sTop / _member.TotalHeight * _member.ViewHeight;
        var maxDeltaY = _member.ViewHeight - _member.$VHand.height();
        if (offsetY > maxDeltaY) {
            offsetY = maxDeltaY;
        }
        else if (offsetY < 0) {
            offsetY = 0;
        }
        switch (_member.$host.css('position')) {
            case "static":
            case "none":
                _member.$VHand.css('margin-top', offsetY);
                //_member.$VBar.offset({ top: _member.$host.offsetParent().top, left: _member.$host.width() - _member.$VBar.width() });
                break;
            case "absolute":
            case "relative":
            case "fixed":
                _member.$VHand.css('margin-top', offsetY);
                _member.$VBar.css('top', _sTop);
                break;
        }
    }
}
//#endregion

//#region 气泡
function Bubble(_$srcElm, _opt) {
    Controller.apply(this, [_$srcElm, _opt]);
    var instance = this;
    this.Option = $.extend({}, BubbleOption, this.Option, _opt);

    this._member.$rootElm.addClass(this.Option.Style).addClass(this.Option.ExStyle).css('display', 'hidden');
    switch (this.Option.Position) {
        case 'right-up':
            this._member.$rootElm.addClass('RightUp');
            break;
        case 'right-down':
            this._member.$rootElm.addClass('RightDown');
            break;
    }
    this._member.$rootElm.appendTo('body');
    this._member.$target = $('body');
    this._member.MousePos = { top: 0, left: 0 };

    this.Visible = false;
    instance.LastVisible = false;

    function onHostScroll() {
        instance.hide();
    };

    function checkStatus() {
    	/// <summary>
    	/// 检查可见状态
    	/// </summary>
        /// <param name="_dely"></param>
        if (instance.Visible === true) {
            instance._member.$rootElm.css('display', 'inline-block');
            if (instance.Visible !== instance.LastVisible) {
                instance.LastVisible = true;
                instance._member.$rootElm.removeClass(instance.Option.AniOut).addClass(instance.Option.AniIn);
            }
        }
        else if (instance.Visible === false&&instance.Visible !== instance.LastVisible) {
            instance.LastVisible = false;
            instance._member.$rootElm.removeClass(instance.Option.AniIn).addClass(instance.Option.AniOut);
        }
    };

    function followMouse(ev) {
    	/// <summary>
    	/// 追踪记录鼠标位置
    	/// </summary>
        /// <param name="ev"></param>
        instance._member.MousePos = { top: ev.clientY, left: ev.clientX };
    }

    function repos() {
    	/// <summary>
    	/// 重新定位气泡位置
        /// </summary>
        if (instance.Option.FollowMouse) {
            instance._member.$rootElm.offset(instance._member.MousePos);
        }
        else {
            LayoutHelper.PosToBody(instance._member.$target, instance._member.$rootElm, instance.Option.Position, instance.Option.Offset);
        }
    }

    instance._member.vf_onDispose=function() {
        $('body').off('mousemove', followMouse);
        instance._member.$rootElm.parent().off('mousewheel', onHostScroll);
        clearInterval(instance.checker);
        instance._member.$target = null;  //断开对target的连接，防止被无意中dispose
        instance.checker = null;
        instance = null;
    }

    instance.af_setValue = function (_cnt) {
        if (ObjHelper.IsEmpty(_cnt)) {
            return instance._member.$rootElm.html();
        }
        if (typeof _cnt == 'function') {
            instance._member.CntLoader = _cnt;
            instance._member.$rootElm.html('正在加载...');
        }
        else {
            instance._member.$rootElm.html(_cnt);
        }
    }

    instance._member.vf_onShow = function () {
        if (instance.CntLoader) {
            instance._member.$rootElm.html(instance._member.CntLoader.call([instance.$target]));
        }
        checkStatus();
        repos();
    };

    instance._member.vf_onHide = function () {
        checkStatus();
    };

    this.Content = function (_cnt) {
        /// <summary>
        /// 获取或设置气泡中的内容或内容加载方法
        /// </summary>
        /// <param name="_cnt"></param>
        if (ObjHelper.IsEmpty(_cnt)) {
            return this._member.$rootElm.html();
        }
        if (typeof _cnt == 'function') {
            this._member.CntLoader = _cnt;
            this._member.$rootElm.html('正在加载...');
        }
        else {
            this._member.$rootElm.html(_cnt);
        }
        return this;
    };

    this.Attach = function (_elm) {
        /// <summary>
        /// 按预定布局模式将气泡附着到目标元素
        /// </summary>
        /// <param name="_$elm" type="HtmlElement,jQuery DOM,Controller"></param>
        if (_elm instanceof Controller) {
            this._member.$target = _elm.dom();
        }
        else if (ObjHelper.IsElement(_elm)) {
            this._member.$target = $(_elm);
        }

        if (instance.Visible === true) {
            repos();
        }
        return this;
    };

    this.ExStyle = function (_style) {
        this._member.$rootElm.removeClass(this.Option.ExStyle);
        this.Option.ExStyle = _style;
        this._member.$rootElm.addClass(this.Option.ExStyle);
        return this;
    }

    if (this.Option.FollowMouse === true) {
        $('body').on('mousemove', followMouse);
    }
    this.checker = setTimeout(checkStatus, 200);
    this._member.$rootElm.parent().on('mousewheel', onHostScroll);
    this.constructor = Bubble;
    return this;
}

Bubble.prototype = Controller.prototype;
var BubbleOption={
    Style: 'Bubble', //气泡的基本样式
    ExStyle:'Error', //气泡的扩展样式Error,Tip,Message,Warning
    AniIn: 'fladeInUp', //显示时的动画样式
    AniOut: 'fladeOutUp', //隐藏时的动画样式
    FollowMouse:false,  //是否跟随鼠标位置
    Position: 'right-up', //显示位置
    Offset:{top:-10,left:-20} //偏移量
}
//#endregion

//#region 支持安全Html格式的富文本编辑框
function HtmlEditor(_srcElm, _opt) {
    Controller.apply(this, [_srcElm, _opt]);
    var instance = this;
    this.Option = $.extend({}, this.Option, HtmlEditorOption, _opt);

    this._member.$rootElm.attr('contenteditable', true)
        .addClass(instance.Option.Style)
        .addClass(instance.Option.ExStyle);
    
    instance.af_getValue = function (_v) {
        return instance._member.$rootElm.html();
    };
    instance.af_setValue = function (_newValue) {
        //TODO：执行必要的输入过滤（防XSS攻击）
        instance._member.$rootElm.html(_newValue);
    };
    instance._member.vf_disable = function () {
        instance._member.$rootElm.addClass("disabled").attr("contenteditable", false);
    };
    instance._member.vf_enable = function () {
        instance._member.$rootElm.removeClass("disabled").attr("contenteditable", true);
    };
    instance._member.vf_onFocus = function (ev) {
    };
    instance._member.vf_onDispose=function(){

    };
    instance._member.vf_reset = function () {
        instance._member.$rootElm.html("");
    }

    this.constructor = HtmlEditor;
    return this;
}
HtmlEditor.prototype = Controller.prototype;
HtmlEditorOption = {
    Style: 'HtmlEditor', //基础样式
    ExStyle: '' //可定制的主体样式
}
//#endregion

//#region 评级控件
function Rating(_srcElm, _opt) {
    Controller.apply(this, [_srcElm, _opt]);
    var instance = this;
    this.Option = $.extend({}, this.Option, RatingOption, _opt);
    instance._member.Stars = [];
    instance.SelectedIndex = -1;

    instance._member.$rootElm.addClass(instance.Option.Style);

    var n = 0;
    for (var i = instance.Option.MinValue; i <= instance.Option.MaxValue; i += instance.Option.ValueStep) {
        instance._member.Stars.push({
            Dom: $("<i></i>").attr('Index',n).addClass(instance.Option.StarStyle),
            Value: i,
            Describe: instance.Option.Describe[n]
        });
        instance._member.$rootElm.append(instance._member.Stars[n].Dom);
        instance._member.$rootElm.append(instance._member.Describe);
        n++;
    }
    instance._member.Describe = $("<h5></h5>").appendTo(instance._member.$rootElm);
    render(-1);

    function render(i) {
        if ($.isNumeric(i) && i >= 0 && i < instance._member.Stars.length) {
            var hotStar = instance._member.$rootElm.children('i').eq(i);
            hotStar.addClass(instance.Option.StyleOnSelected).show().prevAll("i").addClass(instance.Option.StyleOnSelected).show();
            if (instance.IsDisabled == true) {
                hotStar.nextAll("i").removeClass(instance.Option.StyleOnSelected).hide();
            }
            else {
                hotStar.nextAll("i").removeClass(instance.Option.StyleOnSelected);
            }

            if (instance.Option.ShowDescribe == true) {
                instance._member.Describe.html(instance._member.Stars[i].Value + "-" + instance._member.Stars[i].Describe);
            }
            else {
                instance._member.Describe.html('');
            }
        }
        else {
            instance._member.$rootElm.children('i').removeClass(instance.Option.StyleOnSelected);
            if (instance.Option.ShowDescribe == true) {
                instance._member.Describe.html(instance.Option.DefaultDescribe);
            }
            else {
                instance._member.Describe.html('');
            }
        }
    };

    function onMove(ev) {
        if (instance.IsDisabled) {
            return;
        }
        var t = $(ev.target);
        var i = t.attr("Index");
        render(i);
        //if ($.isNumeric(i) && i >= 0 && i < instance._member.Stars.length) {
        //    t.addClass(instance.Option.StyleOnSelected).prevAll("i").addClass(instance.Option.StyleOnSelected);
        //    t.nextAll("i").removeClass(instance.Option.StyleOnSelected);
        //    if (instance.Option.ShowDescribe == true) {
        //        instance._member.Describe.html(instance._member.Stars[i].Value + "-" + instance._member.Stars[i].Describe);
        //    }
            
        //}
        t = null;
    }

    function onMoveOut(ev) {
        if (instance.IsDisabled) {
            return;
        }
        render(instance.SelectedIndex);
    }

    instance._member.vf_onDispose=function() {
        instance._member.$rootElm.off("mousemove", onMove).off("mouseleave", onMoveOut);
        instance = null;
    };

    instance._member.vf_reset = function () {
        //instance._member.$rootElm.children("i").removeClass(instance.Option.StyleOnSelected);
        //if (instance.Option.ShowDescribe == true) {
        //    instance._member.Describe.html(instance.Option.DefaultDescribe);
        //}
        render(-1);
    }

    instance.af_setValue = function (_newValue) {
        for (var i = 0, n = instance._member.Stars.length; i < n; i++) {
            if (_newValue === instance._member.Stars[i].Value) {
                var $elm = instance._member.Stars[i].Dom;
                instance.SelectedIndex = $elm.attr('Index');
                render(instance.SelectedIndex);
                return;
            }
        }
        instance._member.vf_reset();
    }

    instance.af_getValue = function (_v) {
        if (instance.SelectedIndex < 0) {
            return null;
        }
        else {
            return instance._member.Stars[instance.SelectedIndex].Value;
        }
    }

    instance._member.vf_onClick = function (ev) {
        if (!ev||!ev.target) {
            return;
        }
        var t = $(ev.target);
        var i = t.attr("Index");
        if (ObjHelper.IsEmpty(i) || i < 0 || i >= instance._member.Stars.length) {
            return;
        }
        instance.val(instance._member.Stars[i].Value);
    }

    instance._member.vf_disable = function () {
        render(instance.SelectedIndex);
    };

    instance._member.vf_enable = function () {
        render(instance.SelectedIndex);
    }

    this.Select = function (_index) {
    	/// <summary>
    	/// 获取或设置被选中的评分项
    	/// </summary>
        /// <param name="_index"></param>
        if ($.isNumeric(_index) && _index >= 0 && _index < instance._member.Stars.length) {
            instance.val(instance._member.Stars[_index].Value);
        }
        else {
            return instance._member.Stars[instance.SelectedIndex];
        }
    }

    this._member.$rootElm.on('mousemove', onMove).on('mouseleave', onMoveOut);

    this.constructor = Rating;
    return this;
};
Rating.prototype = Controller.prototype;

var RatingOption = {
    Style: 'Rating', //基本样式
    StarStyle: 'Star',  //未选中的等级样式
    StyleOnHover:'', //鼠标悬浮时的样式
    StyleOnSelected: 'selected', //选中的附加等级样式
    MinValue:1,  //最小值
    MaxValue: 4, //最大值
    ValueStep: 1, //值步进
    ShowDescribe:true,  //是否显示每个等级对应的说明
    Describe: ['不重要不紧急', '重要，不紧急', '不重要，紧急', '重要且紧急'],  //每个值对应的说明文字
    DefaultDescribe: '(请评级)', //没有值时的默认提示信息
    Propagation:true
}
//#endregion

//#region 简单文本输入框
function Textbox(_srcInput, _opt) {
    if (ObjHelper.IsEmpty(_srcInput)||!$(_srcInput).is('input')) {
        _srcInput = $("<input>");
    }
    Controller.call(this, _srcInput, _opt);
    var instance = this;

    function onkeyEnter(ev) {
        //确定输入值
        instance.val(instance._member.$rootElm.val());
    };
    function onkeyEsc(ev) {
        //取消输入值
        instance.val(instance._member.Value, false);
    };

    instance._member.vf_onBlur = function () {
        //确定输入值
        instance.val(instance._member.$rootElm.val());
    };

    instance.af_setValue = function (v) {
        instance._member.$rootElm.val(v);
    };

    instance._member.vf_reset = function () {
        instance._member.$rootElm.val('');
    };

    instance._member.vf_onDispose = function () {
        KeybordHelper.off(instance._member.$rootElm, 'enter', onkeyEnter);
        KeybordHelper.off(instance._member.$rootElm, 'esc', onkeyEsc);
    };

    KeybordHelper.on(instance._member.$rootElm, 'enter', onkeyEnter);
    KeybordHelper.on(instance._member.$rootElm, 'esc', onkeyEsc);
    this.constructor = Textbox;
    return this;
};
Textbox.prototype = Controller.prototype;
//#endregion